﻿using Consul;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using M.Core.RmoteCall.Log;
using static M.Core.ClientEventMgr;

namespace M.Core
{

    public class ServiceDiscovery
    {
        //服务是否初始化完毕
        public static bool Init { get; set; } = false;
        private static ServiceDiscovery instance;

        public static ServiceDiscovery GetInstance()
        {
            return instance;
        }

        public ServiceDiscovery([FromServices]ConsulContext consulContext)
        {
            this.consulContext = consulContext;
            instance = this;
        }

        #region 同步服务列表信息
        public void StartSyncServiceList()
        {
            Poll();
        }

        /// <summary>
        /// 服务名与主机IP端品无数据等信息
        /// </summary>
        System.Collections.Concurrent.ConcurrentDictionary<String, CatalogService[]> keyValuePairs = new System.Collections.Concurrent.ConcurrentDictionary<string, CatalogService[]>();

        /// <summary>
        /// 服务名列表最后的变更位置
        /// </summary>
        ulong pollLastIndex = 0;


        /// <summary>
        /// 最新的服务名称列表
        /// </summary>
        string[] serviceNameList;

        /// <summary>
        /// 定时拉取服务名列表信息并检查是否变更,并保存变更和变更位置
        /// </summary>
        public async void Poll()
        {
            while (true)
            {
                var service = await consulContext.consulClient.Catalog.Services();

                if (service.StatusCode == HttpStatusCode.OK)
                {
                    if (pollLastIndex != service.LastIndex)
                    {
                        pollLastIndex = service.LastIndex;

                        var lastKeys = service.Response.Keys.ToArray();
                        foreach (var item in service.Response)
                        {
                            await Check(item.Key);
                        }
                        if (this.serviceNameList!=null)
                        {
                            var removes= this.serviceNameList.Except(lastKeys);
                            foreach (var item in removes)
                            {
                                await Check(item);
                            }
                        }

                        this.serviceNameList = lastKeys;
                    }
                }
                Init = true;
                await Task.Delay(500);
            }
        }

        /// <summary>
        /// 服务资源最后一次的变更位置
        /// </summary>
        Dictionary<string, ulong> lastIndexMap = new Dictionary<string, ulong>();

        /// <summary>
        /// 检查拉取的服务资源并检查是否变更,并保存变更和变更位置
        /// </summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        private async Task Check(string serviceName)
        {
            if (serviceName.Equals("consul")) return;

            var services = await consulContext.consulClient.Catalog.Service(serviceName);
            if (services.StatusCode == HttpStatusCode.OK)
            {
                if (lastIndexMap.ContainsKey(serviceName))
                {
                    var lastIndex = lastIndexMap[serviceName];
                    if (lastIndex != services.LastIndex)
                    {
                        lastIndexMap[serviceName] = services.LastIndex;

                       ServiceChange(serviceName, services.Response);
                    }
                }
                else
                {
                    lastIndexMap[serviceName] = services.LastIndex;
                    ServiceChange(serviceName, services.Response);
                }
            }
        }

        /// <summary>
        /// 服务资源变更后需要发出通知事件,并保存变更信息
        /// </summary>
        /// <param name="serviceName"></param>
        /// <param name="response"></param>
        private void ServiceChange(string serviceName, CatalogService[] response)
        {
            CatalogService[] oldResponse = null;
            if (keyValuePairs.TryGetValue(serviceName, out oldResponse))
            {
                var removes = oldResponse.Except(response, new CatalogServiceExcept());
                var adds = response.Except(oldResponse, new CatalogServiceExcept());


                foreach (var item in adds)
                {
                    ClientEventMgr.GetInstance().SendEvent(ChangeType.Add, item);
                }
                foreach (var item in removes)
                {
                    ClientEventMgr.GetInstance().SendEvent(ChangeType.Remove, item);
                }


                keyValuePairs.TryUpdate(serviceName, response, oldResponse);
            }
            else
            {
                foreach (var item in response)
                {
                    ClientEventMgr.GetInstance().SendEvent(ChangeType.Add, item);
                }

                keyValuePairs.TryAdd(serviceName, response);
            }
        }
        private class CatalogServiceExcept : IEqualityComparer<CatalogService>
        {
            public bool Equals(CatalogService x, CatalogService y)
            {
                return x.ServiceID.Equals(y.ServiceID);
            }

            public int GetHashCode(CatalogService obj)
            {
                return obj.ServiceID.GetHashCode();
            }
        }
        #endregion

        /// <summary>
        /// 轮询值 接服务主机来分开放好点
        /// </summary>
        private int index = 1;
        private ConsulContext consulContext { get; set; }

        internal CatalogService[] Get(string AppName)
        {
            CatalogService[] oldResponse = null;
            keyValuePairs.TryGetValue(AppName, out oldResponse);

            return oldResponse;
        }

        internal IPEndPoint GetHostByRoll(string appName, float version)
        {
            CatalogService[] service = null;
            if (keyValuePairs.TryGetValue(appName,out service))
            {
                var response = service.Where(i => float.Parse(i.ServiceMeta["Version"]) >= version).ToArray();

                if (response.Length > 0)
                {
                    var pos = System.Threading.Interlocked.Increment(ref index) % response.Length;

                    var item = response[pos];

                    return new IPEndPoint(IPAddress.Parse(item.ServiceAddress), item.ServicePort);
                }
            }

            throw new EntryPointNotFoundException("未找到注册服务节点信息");
        }

        internal IPEndPoint GetRpcHostByRoll(string appName, float version)
        {
            CatalogService[] service = null;
            if (keyValuePairs.TryGetValue(appName, out service))
            {
                var response = service.Where(i => float.Parse(i.ServiceMeta["Version"]) >= version).ToArray();

                if (response.Length > 0)
                {
                    var pos = System.Threading.Interlocked.Increment(ref index) % response.Length;

                    var item = response[pos];
                    var thriftPort = Convert.ToInt32(item.ServiceMeta["ThriftPort"]);

                    return new IPEndPoint(IPAddress.Parse(item.ServiceAddress), thriftPort);
                }
            }

            throw new EntryPointNotFoundException("未找到注册服务节点信息");
        }

    }
}
